package com.qunar.wan.support.lucene;

import com.chenlb.mmseg4j.analysis.MaxWordAnalyzer;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.*;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.SimpleFSLockFactory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;

/**
 * Created by hiCyoung on 13-11-30.
 */
public class LuceneContext {
    private static Logger logger = LoggerFactory.getLogger(LuceneContext.class);
    private static String INDEX_FILE_PATH;
    private static Directory dir;
    private static Analyzer analyzer;
    private static IndexWriterConfig iwc;
    private static IndexWriter indexWriter;
    private static IndexReader indexReader;
    private static IndexSearcher searcher;
    private static Version version;

    private LuceneContext() {
    }

    /**
     * 系统启动时调用该方法初始化LuceneContext
     * 遵循fail-fast原则
     */
    static {
        long start = System.currentTimeMillis();
        INDEX_FILE_PATH = System.getProperty("catalina.base") + System.getProperty("file.separator") + "index.files";
        version = Version.LUCENE_43;
        analyzer = new MaxWordAnalyzer();
        iwc = new IndexWriterConfig(version, analyzer);
        iwc.setWriteLockTimeout(6000);
        if (logger.isDebugEnabled()) {
            iwc.setInfoStream(System.out);
        }
        try {
            dir = FSDirectory.open(new File(INDEX_FILE_PATH));
            dir.setLockFactory(new SimpleFSLockFactory());
        } catch (IOException e) {
            logger.error("LuceneContext open dir异常{}", e);
        }
        try {
            initIndexWriter();
        } catch (IOException e) {
            logger.error("初始化LuceneContext indexWriter异常:{}", e);
        }
        try {
            indexReader = DirectoryReader.open(indexWriter, true);
        } catch (IOException e) {
            logger.error("初始化LuceneContext indexReader异常:{}", e);
        }
        searcher = new IndexSearcher(indexReader);
        logger.info("初始化LuceneContext耗时：{}ms", System.currentTimeMillis() - start);
    }

    /**
     * LuceneContext index info
     */
    public static void showDocsInfo() throws IOException {
        int numDoc = LuceneContext.getIndexReader().numDocs();
        logger.info("Init LuceneContext maxdocs num is : {}", LuceneContext.getIndexReader().maxDoc());
        logger.info("Init LuceneContext numdocs num is : {}", numDoc);
        logger.info("Init LuceneContext deletedDocs num is : {}", LuceneContext.getIndexReader().numDeletedDocs());
        logger.info("Init LuceneContext RefCount is : {}", LuceneContext.getIndexReader().getRefCount());
    }

    /**
     * 获取Lucene Version
     */
    public static Version getVersion() {
        return version;
    }

    /**
     * 获取Lucene分词策略
     */
    public static Analyzer getAnalyzer() {
        return analyzer;
    }

    /**
     * 获取Lucene IndexWriter,单例
     * 
     * @return
     * @throws java.io.IOException
     */
    private static void initIndexWriter() throws IOException {
        if (IndexWriter.isLocked(dir)) {
            IndexWriter.unlock(dir);
        }
        if (indexWriter == null) {
            try {
                indexWriter = new IndexWriter(dir, iwc);
            } catch (LockObtainFailedException e) {
                logger.info("Lucene获取IndexWriter异常");
                forceDeleteLockFile();
                // try again
                try {
                    indexWriter = new IndexWriter(dir, iwc);
                } catch (Throwable throwable) {
                    logger.error("第二次获取Lucene Indexritter异常");
                }
            }
        }
    }

    /**
     * 获取Lucene IndexReader,单例
     *
     * @return IndexReader
     * @throws java.io.IOException
     */
    public static IndexReader getIndexReader() throws IOException {
        if (indexReader == null) {
            indexReader = DirectoryReader.open(dir);
        }
        return indexReader;
    }

    /**
     * 提交所有的变化（删除/新增Document）生效可见，实现NRT 注意：并没有写入到文件 如果此时程序出现故障而关闭LuceneContext，并且之前没有执行过commit操作，修改将会丢失
     *
     * @throws java.io.IOException
     */
    public static void commitChangesVisible() throws IOException {
        IndexReader newReader = DirectoryReader.openIfChanged((DirectoryReader) indexReader, indexWriter, true);
        if (newReader != null) {
            indexReader.close();
            indexReader = newReader;
            searcher = new IndexSearcher(indexReader);
        }
    }

    /**
     * 将修改持久化到文件
     *
     * @throws java.io.IOException
     */
    public static void flush2Files() throws IOException {
        indexWriter.forceMergeDeletes();
        indexWriter.forceMerge(3);
        indexWriter.prepareCommit();
        indexWriter.commit();
        commitChangesVisible();
    }

    /**
     * 获取Lucene IndexWriter,单例
     *
     * @return
     * @throws java.io.IOException
     */
    public static IndexWriter getIndexWriter() throws IOException {
        if (IndexWriter.isLocked(dir)) {
            IndexWriter.unlock(dir);
        }
        if (indexWriter == null) {
            try {
                indexWriter = new IndexWriter(dir, iwc);
            } catch (LockObtainFailedException e) {
                logger.info("Lucene获取IndexWriter异常");
                forceDeleteLockFile();
                indexWriter = new IndexWriter(dir, iwc);
            }
        }
        return indexWriter;
    }

    /**
     * 删除write.lock文件，解决LockObtainFailedException
     */
    private static void forceDeleteLockFile() {
        File file = new File(INDEX_FILE_PATH + File.separator + "write.lock");
        if (file.exists() && file.isFile()) {
            file.delete();
        }
    }

    /**
     * 获取Lucene IndexSearcher,单例
     *
     * @return
     * @throws java.io.IOException
     */
    public static IndexSearcher getSearcher() throws IOException {
        if (searcher == null) {
            searcher = new IndexSearcher(indexReader);
        }
        return searcher;
    }

    /**
     * 删除Document，立即生效可见（并未立即更改索引文件）
     *
     * @param term
     * @throws java.io.IOException
     */
    public static void deleteDocument(Term term) throws IOException {
        indexWriter.deleteDocuments(new TermQuery(term));
        commitChangesVisible();
    }

    /**
     * 清空所有索引（并未立即更改索引文件）
     *
     * @throws java.io.IOException
     */
    public static void emptyIndexs() throws IOException {
        indexWriter.deleteAll();
        commitChangesVisible();
    }

    public static void destory() throws IOException {
        dir.close();
        if (indexReader != null) {
            indexReader.close();
        }
        if (indexWriter != null) {
            indexWriter.close();
        }
    }

}
